package com.bzkj.basicinfo.service.impl.yxl;

import cn.hutool.core.collection.CollectionUtil;
import com.bzkj.basicinfo.domain.basic.BasicStoStock;
import com.bzkj.basicinfo.domain.basic.vo.StoUnitVo;
import com.bzkj.basicinfo.domain.wms.WmsWarningTypeInfoEntity;
import com.bzkj.basicinfo.domain.wms.vo.ExportWMSWarehouseInDetailVo;
import com.bzkj.basicinfo.domain.wms.vo.ExportWMSWarehouseOutDetailVo;
import com.bzkj.basicinfo.domain.yxl.PdaWarehouseMove;
import com.bzkj.basicinfo.domain.yxl.YxlBillsList;
import com.bzkj.basicinfo.domain.yxl.vo.ExportInBillsList;
import com.bzkj.basicinfo.domain.yxl.vo.ExportOutBillsList;
import com.bzkj.basicinfo.domain.yxl.vo.ExportSalableDrugVo;
import com.bzkj.basicinfo.domain.yxl.vo.YxlBillsReportVo;
import com.bzkj.basicinfo.mapper.*;
import com.bzkj.basicinfo.service.yxl.YxlReportService;
import com.bzkj.common.core.domain.AjaxResult;
import com.bzkj.common.core.redis.RedisCache;
import com.bzkj.common.enums.DataSourceType;
import com.bzkj.common.utils.DataProcessCommonUtils;
import com.bzkj.common.utils.DateUtils;
import com.bzkj.common.utils.StringUtils;
import com.bzkj.common.utils.poi.ExcelUtil;
import com.bzkj.framework.datasource.DynamicDataSourceContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisConnectionUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Field;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.function.Supplier;

/**
 * @Author：swj
 * @Desc
 * @Date：2025/8/13 10:34
 */
@Service
@Slf4j
public class YxlReportServiceImpl implements YxlReportService {
    @Autowired
    private YxlReportMapper yxlReportMapper;

    @Autowired
    private WmsWarningTypeInfoMapper warningTypeInfoMapper;

    @Autowired
    private BasicStoStockMapper basicStoStockMapper;

    @Autowired
    private WarehouseMapper warehouseMapper; // 注入仓库数据访问层

    @Autowired
    private StoAreaMapper stoAreaMapper; // 注入库区数据访问层

    @Override
    public List<YxlBillsReportVo> queryTop10(String type) {
        List<YxlBillsReportVo> resultVos;
        if (type.equals("1")) {
            resultVos = yxlReportMapper.queryDayTop10();
        } else if (type.equals("2")) {
            resultVos = yxlReportMapper.queryWeekTop10();
        } else {
            resultVos = yxlReportMapper.queryMonthTop10();
        }
        if (CollectionUtil.isEmpty(resultVos)) {
            return Collections.emptyList();
        } else {
            return resultVos;
        }
    }

    @Override
    public AjaxResult lowInventory(Integer pageNum, Integer pageSize) {
        List<YxlBillsReportVo> resultVo = new ArrayList<>();
        //查询预警信息
        WmsWarningTypeInfoEntity entity = warningTypeInfoMapper.selectWmsWarningTypeInfoById(3L);
        //手动切换数据源
//        try {
//            DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.SLAVE.name());
//            log.info("手动切换到从库，当前数据源：{}", DynamicDataSourceContextHolder.getDataSourceType());
            if (entity.getWarningTime() != null && entity.getWarningTime() > 0) {
                //查询库存余额
                resultVo = yxlReportMapper.lowInventoryWarning(entity.getWarningTime());
            }
//        } finally {
//            DynamicDataSourceContextHolder.clearDataSourceType();
//        }

        return AjaxResult.success(DataProcessCommonUtils.getTableInfo(resultVo, pageNum, pageSize));
    }

    @Override
    public AjaxResult expWarning(Integer pageNum, Integer pageSize) {
        List<YxlBillsReportVo> resultVo = new ArrayList<>();
        //查询预警信息
        WmsWarningTypeInfoEntity entity = warningTypeInfoMapper.selectWmsWarningTypeInfoById(1L);
        //手动切换数据源
//        try {
//            DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.SLAVE.name());
//            log.info("手动切换到从库，当前数据源：{}", DynamicDataSourceContextHolder.getDataSourceType());
            if (entity.getWarningTime() != null && entity.getWarningTime() > 0) {
                Integer warningTime = entity.getWarningTime();
                // 获取当前日期
                LocalDate currentDate = LocalDate.now();
                // 计算当前日期向后推90天的日期
                LocalDate targetDate = currentDate.plusDays(warningTime);
                // 将日期转换为字符串（例如："yyyy-MM-dd"格式）
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
                // 转换为字符串格式
                String startDate = currentDate.format(formatter);
                String endDate = targetDate.format(formatter);
                resultVo = yxlReportMapper.expWarning(startDate, endDate);
            }
//        } finally {
//            DynamicDataSourceContextHolder.clearDataSourceType();
//        }
        return AjaxResult.success(DataProcessCommonUtils.getTableInfo(resultVo, pageNum, pageSize));
    }

    @Override
    public AjaxResult realTime() {
        Map<String, Integer> map = new HashMap<>();
        //入库单据统计
        YxlBillsReportVo inTotal = yxlReportMapper.countInBills();
        if (Objects.nonNull(inTotal) && inTotal.getInTotal() != 0) {
            map.put("inTotal", inTotal.getInTotal());
        } else {
            map.put("inTotal", 0);
        }
        //出库单据统计
        YxlBillsReportVo outTotal = yxlReportMapper.countOutBills();
        if (Objects.nonNull(outTotal) && outTotal.getOutTotal() != 0) {
            map.put("outTotal", outTotal.getOutTotal());
        } else {
            map.put("outTotal", 0);
        }
        //入库商品数统计
        YxlBillsReportVo inDrugTotal = yxlReportMapper.countInDrugNum();
        if (Objects.nonNull(inDrugTotal) && inDrugTotal.getInDrugTotal() != 0) {
            map.put("inDrugTotal", inDrugTotal.getInDrugTotal());
        } else {
            map.put("inDrugTotal", 0);
        }
        //出库商品数统计
        YxlBillsReportVo outDrugTotal = yxlReportMapper.countOutDrugNum();
        if (Objects.nonNull(outDrugTotal) && outDrugTotal.getOutDrugTotal() != 0) {
            map.put("outDrugTotal", outDrugTotal.getOutDrugTotal());
        } else {
            map.put("outDrugTotal", 0);
        }
        //商品品种数统计
        YxlBillsReportVo spTotal = yxlReportMapper.countSpTotal();
        if (spTotal.getSpTotal() != 0) {
            map.put("spTotal", spTotal.getSpTotal());
        } else {
            map.put("spTotal", 0);
        }
        return AjaxResult.success(map);
    }

    @Override
    public AjaxResult queryWarehouseOutInDetail(Integer type, String stoId, String startDate, String endDate,
                                                String drugCD, String drugBatchNo, String drugName, String userName,
                                                String drugJwh, Integer pageNum, Integer pageSize) {
        startDate = startDate + " 00:00:00";
        endDate = endDate + " 23:59:59";
        List<HashMap<String, Object>> mapList = yxlReportMapper.queryWarehouseOutInDetail(type, stoId, startDate, endDate,
                drugCD, drugBatchNo, drugName, userName, drugJwh);
        return AjaxResult.success(DataProcessCommonUtils.getTableInfo(mapList, pageNum, pageSize));
    }

    @Override
    public AjaxResult exportWarehouseOutInDetail(Integer type, String stoId, String startDate, String endDate,
                                                 String drugCD, String drugBatchNo, String drugName, String userName,
                                                 String drugJwh, String selectedColumns, HttpServletResponse response) {
        List<HashMap<String, Object>> mapList = yxlReportMapper.queryWarehouseOutInDetail(type, stoId, startDate, endDate,
                drugCD, drugBatchNo, drugName, userName, drugJwh);
        String[] allColumns;
        List<ExportWMSWarehouseOutDetailVo> outList = new ArrayList<>();
        List<ExportWMSWarehouseInDetailVo> inList = new ArrayList<>();
        if (type == 1) {
            outList = convertAreaHouseDetailMapToVoList(mapList);
            allColumns = getAllColumns(ExportWMSWarehouseOutDetailVo.class);
        } else {
            inList = convertAreaHouseInDetailMapToVoList(mapList);
            allColumns = getAllColumns(ExportWMSWarehouseOutDetailVo.class);
        }
        // 将 selectedColumns 转换为 Set，以便更快地检查是否包含某个属性
        String[] split = selectedColumns.split(",");
        Set<String> selectedSet = new HashSet<>(Arrays.asList(split));
        // 使用列表来存储未选择的列
        List<String> remainingColumns = new ArrayList<>();
        // 遍历所有列，并将未选择的列添加到 remainingColumns 列表中
        for (String column : allColumns) {
            if (!selectedSet.contains(column)) {
                remainingColumns.add(column);
            }
        }
        // 将 remainingColumns 转换回数组
        String[] finalColumns = remainingColumns.toArray(new String[0]);

        if (CollectionUtil.isNotEmpty(outList)) {
            ExcelUtil<ExportWMSWarehouseOutDetailVo> util = new ExcelUtil<>(ExportWMSWarehouseOutDetailVo.class);
            util.hideColumn(finalColumns);
            util.exportExcel(response, outList, "出库明细", "出库明细", "出库明细");
        }
        if (CollectionUtil.isNotEmpty(inList)) {
            ExcelUtil<ExportWMSWarehouseInDetailVo> util = new ExcelUtil<>(ExportWMSWarehouseInDetailVo.class);
            util.hideColumn(finalColumns);
            util.exportExcel(response, inList, "入库明细", "入库明细", "入库明细");
        }
        return AjaxResult.success();
    }

    @Override
    public AjaxResult queryInBillsList(String billsId, String stoId, String startDate, String endDate, String billsCd,
                                       String userName, String isQr, Integer pageNum, Integer pageSize) {
        Map<String, Object> map = new HashMap<>();
        //已完成单据
        List<YxlBillsList> billsList = yxlReportMapper.queryInBillsList(billsId, stoId, startDate, endDate, billsCd, userName);
        List<YxlBillsList> list = billsList.stream().filter(i -> "是".equals(i.getIs_qr())).collect(Collectors.toList());
        //未完成单据
        List<YxlBillsList> list1 = billsList.stream().filter(i -> !"是".equals(i.getIs_qr())).collect(Collectors.toList());
        map.put("type1", list);
        map.put("type2", list1);
        return AjaxResult.success(map);
    }

    @Override
    public AjaxResult queryOutBillsList(String billsId, String stoId, String startDate, String endDate, String billsCd,
                                        String userName, String isQr, Integer pageNum, Integer pageSize) {
        Map<String, Object> map = new HashMap<>();
        List<YxlBillsList> billsList = yxlReportMapper.queryOutBillsList(billsId, stoId, startDate, endDate, billsCd, userName);
        List<YxlBillsList> list = billsList.stream()
                .filter(i -> "是".equals(i.getIs_qr()))
                .collect(Collectors.toList());

        List<YxlBillsList> list1 = billsList.stream()
                .filter(i -> !"是".equals(i.getIs_qr()))
                .collect(Collectors.toList());
        map.put("type1", list);
        map.put("type2", list1);
        return AjaxResult.success(map);
    }

    @Override
    public AjaxResult exportInBillsList(String billsId, String stoId, String startDate, String endDate, String billsCd, String userName, String isQr, HttpServletResponse response) {
        List<YxlBillsList> billsList = yxlReportMapper.queryInBillsList(billsId, stoId, startDate, endDate, billsCd, userName);

        Map<Boolean, List<YxlBillsList>> partitioned = billsList.stream()
                .filter(Objects::nonNull) // 过滤null元素，避免NPE
                .collect(Collectors.partitioningBy(i -> "是".equals(i.getIs_qr())));

        Function<YxlBillsList, ExportInBillsList> converter = src -> {
            ExportInBillsList dest = new ExportInBillsList();
            BeanUtils.copyProperties(src, dest);
            return dest;
        };

        List<ExportInBillsList> exportFinishList = partitioned.get(true).stream()
                .map(converter)
                .collect(Collectors.toList());

        List<ExportInBillsList> exportNoFinishList = partitioned.get(false).stream()
                .map(converter)
                .collect(Collectors.toList());

        ExcelUtil<ExportInBillsList> util = new ExcelUtil<>(ExportInBillsList.class);
        util.exportExcel(response, exportFinishList, "已完成入库单据", "入库单据", "已完成入库单据");
        util.exportExcel(response, exportNoFinishList, "未完成入库单据", "入库单据", "未完成入库单据");
        return AjaxResult.success();
    }

    @Override
    public AjaxResult exportOutBillsList(String billsId, String stoId, String startDate, String endDate, String billsCd, String userName, String isQr, HttpServletResponse response) {
        List<YxlBillsList> billsList = yxlReportMapper.queryOutBillsList(billsId, stoId, startDate, endDate, billsCd, userName);
        Map<Boolean, List<YxlBillsList>> partitioned = billsList.stream()
                .filter(Objects::nonNull) // 过滤null元素，避免NPE
                .collect(Collectors.partitioningBy(i -> "是".equals(i.getIs_qr())));

        Function<YxlBillsList, ExportOutBillsList> converter = src -> {
            ExportOutBillsList dest = new ExportOutBillsList();
            BeanUtils.copyProperties(src, dest);
            return dest;
        };

        List<ExportOutBillsList> exportFinishList = partitioned.get(true).stream()
                .map(converter)
                .collect(Collectors.toList());

        List<ExportOutBillsList> exportNoFinishList = partitioned.get(false).stream()
                .map(converter)
                .collect(Collectors.toList());

        ExcelUtil<ExportOutBillsList> util = new ExcelUtil<>(ExportOutBillsList.class);
        util.exportExcel(response, exportFinishList, "已完成出库单据", "出库单据", "已完成出库单据");
        util.exportExcel(response, exportNoFinishList, "未完成出库单据", "出库单据", "未完成出库单据");
        return AjaxResult.success();
    }

    @Override
    public List<PdaWarehouseMove> queryWarehouseMoveList(String userName, String spmch, String batchCode, String startDate, String endDate) {
        List<PdaWarehouseMove> warehouseMoves = yxlReportMapper.queryWarehouseMoveList(userName, spmch, batchCode, startDate, endDate);
        warehouseMoves.stream().forEach(item -> item.setDesc(item.getSpmch() + "从" + item.getFromAreaName() + "的【" + item.getFromJwh() + "】移库到"
                + item.getToAreaName() + "的【" + item.getToJwh() + "】上。"));
        if (CollectionUtil.isEmpty(warehouseMoves)) {
            return new ArrayList<>();
        } else {
            return warehouseMoves;
        }
    }

    @Override
    public AjaxResult queryInChartInfo() {
        Map<String, Object> resultMap = new HashMap<>();
        List<HashMap<String, Object>> mapList = yxlReportMapper.queryInBillTop();
        List<HashMap<String, Object>> hashMaps = yxlReportMapper.selectInNumber();
        List<HashMap<String, Object>> hashMapList = yxlReportMapper.selectInSkuNumber();
        resultMap.put("top", mapList);
        resultMap.put("inWarehouseNum", hashMaps);
        resultMap.put("inSkuNumber", hashMapList);
        return AjaxResult.success(resultMap);
    }

    @Override
    public AjaxResult queryOutChartInfo() {
        Map<String, Object> resultMap = new HashMap<>();
        List<HashMap<String, Object>> mapList = yxlReportMapper.queryOutBillTop();
        List<HashMap<String, Object>> hashMaps = yxlReportMapper.selectOutNumber();
        List<HashMap<String, Object>> hashMapList = yxlReportMapper.selectOutSkuNumber();
        resultMap.put("top", mapList);
        resultMap.put("outWarehouseNum", hashMaps);
        resultMap.put("outSkuNumber", hashMapList);
        return AjaxResult.success(resultMap);
    }

    @Override
    public AjaxResult queryCustomer(String type) {
        List<HashMap<String, Object>> resultVos;
        if (type.equals("1")) {
            resultVos = yxlReportMapper.selectCustomer();
        } else if (type.equals("2")) {
            resultVos = yxlReportMapper.selectCustomerQuarter();
        } else {
            resultVos = yxlReportMapper.selectCustomerYear();
        }
        return AjaxResult.success(resultVos);
    }

    @Override
    public AjaxResult queryCustomerMonthAndYear(String dateRange) {
        if (StringUtils.isBlank(dateRange)) {
            dateRange = DateUtils.getDate().substring(0, 7);
        }
        return AjaxResult.success(yxlReportMapper.selectCustomerYearAndMonth(dateRange));
    }

    @Override
    public AjaxResult queryBarCode(String barCode, String orderId, Integer pageNum, Integer pageSize) {
        List<HashMap<String, Object>> mapList = yxlReportMapper.selectBarCode(barCode, orderId);
        return AjaxResult.success(DataProcessCommonUtils.getTableInfo(mapList, pageNum, pageSize));
    }

    @Override
    public AjaxResult queryBarCodeByParentCode(String parentCode) {
        return AjaxResult.success(yxlReportMapper.selectBarCodeByParentCode(parentCode));
    }

    @Override
    public AjaxResult queryUnsalableDrug(String stoId,  String spbh, String startDate, String endDate, Integer pageNum, Integer pageSize) {
        List<HashMap<String, Object>> mapList = yxlReportMapper.selectDrugInfoList(stoId, spbh, startDate, endDate,"0");
        return AjaxResult.success(DataProcessCommonUtils.getTableInfo(mapList, pageNum, pageSize));
    }

    @Override
    public AjaxResult querySalableDrug(String stoId, String spbh, String startDate, String endDate, Integer pageNum, Integer pageSize) {
        List<HashMap<String, Object>> mapList = yxlReportMapper.selectDrugInfoList(stoId,  spbh, startDate, endDate,"1");
        return AjaxResult.success(DataProcessCommonUtils.getTableInfo(mapList, pageNum, pageSize));
    }

    @Override
    public AjaxResult exportSalableDrug(String stoId,  String spbh, String startDate, String endDate, HttpServletResponse response) {
        List<HashMap<String, Object>> mapList = yxlReportMapper.selectDrugInfoList(stoId,  spbh, startDate, endDate,"1");
        List<ExportSalableDrugVo> drugs = convertBasicMapToVoList(mapList);
        ExcelUtil<ExportSalableDrugVo> util = new ExcelUtil<>(ExportSalableDrugVo.class);
        util.exportExcel(response, drugs, "畅销品", "畅销品", "畅销品");
        return AjaxResult.success();
    }

    @Override
    public AjaxResult exportUnSalableDrug(String stoId,  String spbh, String startDate, String endDate, HttpServletResponse response) {
        List<HashMap<String, Object>> mapList = yxlReportMapper.selectDrugInfoList(stoId,  spbh, startDate, endDate,"0");
        List<ExportSalableDrugVo> drugs = convertBasicMapToVoList(mapList);
        ExcelUtil<ExportSalableDrugVo> util = new ExcelUtil<>(ExportSalableDrugVo.class);
        util.exportExcel(response, drugs, "滞销品", "滞销品", "滞销品");
        return AjaxResult.success();
    }

    private List<ExportSalableDrugVo> convertBasicMapToVoList(Object rows) {
        if (rows == null) {
            return Collections.emptyList();
        }

        if (!(rows instanceof List<?>)) {
            return Collections.emptyList();
        }

        List<?> rowList = (List<?>) rows;
        List<ExportSalableDrugVo> resultList = new ArrayList<>(rowList.size());

        for (Object item : rowList) {
            if (item instanceof Map<?, ?>) {
                Map<?, ?> rowMap = (Map<?, ?>) item;
                ExportSalableDrugVo vo = new ExportSalableDrugVo();
                vo.setSpbh(getStringValue(rowMap, "spbh"));
                vo.setDrugName(getStringValue(rowMap, "DrugName"));
                vo.setDrugBatchNo(getStringValue(rowMap, "DrugBatchNo"));
                vo.setDrugGG(getStringValue(rowMap, "DrugGG"));
                vo.setDw(getStringValue(rowMap, "dw"));
                vo.setTotalSales(getStringValue(rowMap, "TotalSales"));
                vo.setBaozhiqi(getStringValue(rowMap, "baozhiqi"));
                vo.setSxrq(getStringValue(rowMap, "sxrq"));
                vo.setPizhwh(getStringValue(rowMap, "pizhwh"));
                vo.setDrugCD(getStringValue(rowMap, "DrugCD"));
                resultList.add(vo);
            }
        }
        return resultList;
    }

    @Override
    public AjaxResult countUnitNum(String stoId, String type) {
        Map<String, Object> resultMap = new HashMap<>();
        List<HashMap<String, Object>> mapList = yxlReportMapper.countUnitNum("0",stoId, type);
        List<HashMap<String, Object>> mapList1 = yxlReportMapper.countUnitNum("1",stoId, type);
        List<HashMap<String, Object>> mapList2 = yxlReportMapper.countUnitNum("2",stoId, type);
        resultMap.put("type", mapList);
        resultMap.put("type1", mapList1);
        resultMap.put("type2", mapList2);
        return AjaxResult.success(resultMap);
    }

    @Autowired
    private RedisCache redisCache;

    private static final long CACHE_TTL_MINUTES = 20;
    private static final long EMPTY_CACHE_TTL_MINUTES = 2; // 空数据占位缓存时间，防穿透
    private static final String CACHE_KEY_BILLS = "largeScreen:billsInfo";
    private static final String CACHE_KEY_LOW_WARNING = "largeScreen:lowWarning";
    private static final String CACHE_KEY_EXP_WARNING = "largeScreen:expWarning";
    private static final String CACHE_KEY_USER_KPI = "largeScreen:userKpi";
    private static final String CACHE_KEY_SALABLE = "largeScreen:salable";

    // ===================== Redis操作封装 =====================
    private <T> void setCache(String key, T value, long ttlMinutes) {
        try {
            redisCache.setCacheObject(key, value, (int) ttlMinutes, TimeUnit.MINUTES);
            log.info("缓存更新成功 key={} ttl={}分钟", key, ttlMinutes);
        } catch (Exception e) {
            log.error("缓存更新失败 key={}", key, e);
        }
    }

    /**
     * Cache-Aside 模式
     * 先尝试读取缓存，缓存未命中则查数据库，并写入缓存
     */
    private <T> List<T> getCacheOrDb(String cacheKey, Supplier<List<T>> dbSupplier) {
        List<T> list = redisCache.getCacheObject(cacheKey);
        if (list != null && !list.isEmpty()) {
            return list;
        }

        // 缓存未命中 → 查询数据库
        try {
            list = dbSupplier.get();
            if (list == null) list = new ArrayList<>();
        } catch (Exception e) {
            log.error("数据库查询失败 key={}", cacheKey, e);
            list = new ArrayList<>();
        }

        // 写入缓存
        long ttl = list.isEmpty() ? EMPTY_CACHE_TTL_MINUTES : CACHE_TTL_MINUTES;
        setCache(cacheKey, list, ttl);
        return list;
    }

    // ===================== 定时任务刷新缓存 =====================
    @Scheduled(cron = "0 */15 * * * ?") // 每15分钟刷新一次
    public void refreshAllCaches() {
        refreshCacheSafely("刷新大屏单据信息", this::refreshBillsCache);
        refreshCacheSafely("刷新效期预警信息", this::refreshExpWarningCache);
        refreshCacheSafely("刷新低库存预警信息", this::refreshLowInventoryCache);
        refreshCacheSafely("刷新畅销/滞销药品信息", this::refreshSalableCache);
        refreshCacheSafely("刷新用户KPI信息", this::refreshUserKpiCache);
    }

    private void refreshCacheSafely(String taskName, Runnable refreshTask) {
        try {
            log.info("{} 开始", taskName);
            refreshTask.run();
            log.info("{} 完成", taskName);
        } catch (Exception e) {
            log.error("{} 失败", taskName, e);
        }
    }

    // ===================== 各缓存刷新逻辑 =====================
    private void refreshBillsCache() {
        List<YxlBillsList> billsLists = yxlReportMapper.selectLargeScreenList();
        if (billsLists == null) billsLists = new ArrayList<>();
        setCache(CACHE_KEY_BILLS, billsLists, billsLists.isEmpty() ? EMPTY_CACHE_TTL_MINUTES : CACHE_TTL_MINUTES);
    }

    private void refreshExpWarningCache() {
        List<YxlBillsReportVo> resultVo = new ArrayList<>();
        WmsWarningTypeInfoEntity entity = warningTypeInfoMapper.selectWmsWarningTypeInfoById(1L);

        if (entity != null && entity.getWarningTime() != null && entity.getWarningTime() > 0) {
            try {
                DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.SLAVE.name());
                LocalDate currentDate = LocalDate.now();
                LocalDate targetDate = currentDate.plusDays(entity.getWarningTime());
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
                resultVo = yxlReportMapper.expWarning(
                        currentDate.format(formatter), targetDate.format(formatter));
            } finally {
                DynamicDataSourceContextHolder.clearDataSourceType();
            }
        }

        if (resultVo == null) resultVo = new ArrayList<>();
        setCache(CACHE_KEY_EXP_WARNING, resultVo, resultVo.isEmpty() ? EMPTY_CACHE_TTL_MINUTES : CACHE_TTL_MINUTES);
    }

    private void refreshLowInventoryCache() {
        List<YxlBillsReportVo> resultVo = new ArrayList<>();
        WmsWarningTypeInfoEntity entity = warningTypeInfoMapper.selectWmsWarningTypeInfoById(3L);

        if (entity != null && entity.getWarningTime() != null && entity.getWarningTime() > 0) {
            try {
                DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.SLAVE.name());
                resultVo = yxlReportMapper.lowInventoryWarning(entity.getWarningTime());
            } finally {
                DynamicDataSourceContextHolder.clearDataSourceType();
            }
        }

        if (resultVo == null) resultVo = new ArrayList<>();
        setCache(CACHE_KEY_LOW_WARNING, resultVo, resultVo.isEmpty() ? EMPTY_CACHE_TTL_MINUTES : CACHE_TTL_MINUTES);
    }

    private void refreshSalableCache() {
        List<HashMap<String, Object>> mapList1 = yxlReportMapper.selectDrugInfoList(null, null, null, null, "1");
        if (mapList1 == null) mapList1 = new ArrayList<>();
        mapList1.forEach(item -> item.put("flag", "1"));

        List<HashMap<String, Object>> mapList2 = yxlReportMapper.selectDrugInfoList(null, null, null, null, "0");
        if (mapList2 == null) mapList2 = new ArrayList<>();
        mapList2.forEach(item -> item.put("flag", "0"));

        List<HashMap<String, Object>> combinedList = new ArrayList<>();
        combinedList.addAll(mapList1);
        combinedList.addAll(mapList2);

        setCache(CACHE_KEY_SALABLE, combinedList, combinedList.isEmpty() ? EMPTY_CACHE_TTL_MINUTES : CACHE_TTL_MINUTES);
    }

    private void refreshUserKpiCache() {
        List<HashMap<String, Object>> type = warehouseMapper.wmsUserKpi("fixedDataType", "");
        if (type == null) type = new ArrayList<>();
        setCache(CACHE_KEY_USER_KPI, type, type.isEmpty() ? EMPTY_CACHE_TTL_MINUTES : CACHE_TTL_MINUTES);
    }

    // ===================== 接口访问 =====================
    public AjaxResult largeScreenBillsInfo() {
        List<YxlBillsList> billsLists = getCacheOrDb(CACHE_KEY_BILLS, () ->
                yxlReportMapper.selectLargeScreenList());
        return AjaxResult.success(billsLists);
    }

    public AjaxResult largeScreenExpWarning() {
        List<YxlBillsReportVo> resultVo = getCacheOrDb(CACHE_KEY_EXP_WARNING, () -> {
            WmsWarningTypeInfoEntity entity = warningTypeInfoMapper.selectWmsWarningTypeInfoById(1L);
            if (entity == null || entity.getWarningTime() == null || entity.getWarningTime() <= 0) {
                return new ArrayList<>();
            }

            try {
                DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.SLAVE.name());
                LocalDate currentDate = LocalDate.now();
                LocalDate targetDate = currentDate.plusDays(entity.getWarningTime());
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
                return yxlReportMapper.expWarning(
                        currentDate.format(formatter),
                        targetDate.format(formatter));
            } finally {
                DynamicDataSourceContextHolder.clearDataSourceType();
            }
        });
        return AjaxResult.success(resultVo);
    }

    public AjaxResult largeScreenLowInventory() {
        List<YxlBillsReportVo> resultVo = getCacheOrDb(CACHE_KEY_LOW_WARNING, () -> {
            WmsWarningTypeInfoEntity entity = warningTypeInfoMapper.selectWmsWarningTypeInfoById(3L);
            if (entity == null || entity.getWarningTime() == null || entity.getWarningTime() <= 0) {
                return new ArrayList<>();
            }

            try {
                DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.SLAVE.name());
                return yxlReportMapper.lowInventoryWarning(entity.getWarningTime());
            } finally {
                DynamicDataSourceContextHolder.clearDataSourceType();
            }
        });
        return AjaxResult.success(resultVo);
    }

    public AjaxResult queryLargeSalable(String stoId, String drugName, String spbh, String startDate, String endDate) {
        List<HashMap<String, Object>> combinedList = getCacheOrDb(CACHE_KEY_SALABLE, () -> {
            List<HashMap<String, Object>> mapList1 = yxlReportMapper.selectDrugInfoList(null, null, null, null, "1");
            if (mapList1 == null) mapList1 = new ArrayList<>();
            mapList1.forEach(item -> item.put("flag", "1"));

            List<HashMap<String, Object>> mapList2 = yxlReportMapper.selectDrugInfoList(null, null, null, null, "0");
            if (mapList2 == null) mapList2 = new ArrayList<>();
            mapList2.forEach(item -> item.put("flag", "0"));

            List<HashMap<String, Object>> combined = new ArrayList<>();
            combined.addAll(mapList1);
            combined.addAll(mapList2);
            return combined;
        });
        return AjaxResult.success(combinedList);
    }

    public AjaxResult wmsUserKpi(String dataType, String sType) {
        List<HashMap<String, Object>> type = getCacheOrDb(CACHE_KEY_USER_KPI, () ->
                warehouseMapper.wmsUserKpi("1", ""));
        return AjaxResult.success(type);
    }

    @Override
    public AjaxResult queryAreaByStoId(String stoId) {
        return AjaxResult.success(stoAreaMapper.selectStoAreasByWarehouseId(stoId));
    }



    @Override
    public AjaxResult queryUnitByAreaId(String stoId, String areaId) {
        List<StoUnitVo> vos = new ArrayList<>();
        StoUnitVo vo = new StoUnitVo();
        List<HashMap<String, Object>>  mapList = basicStoStockMapper.findWmsWarehouseInfo("2", stoId, "", "", areaId, "G");
        List<HashMap<String, Object>> mapList1 = basicStoStockMapper.findWmsWarehouseInfo("2", stoId, "", "", areaId, "D");
        vo.setTag("G");
        vo.setShelfName("零货货架");
        vo.setChildren(mapList);
        StoUnitVo vo1 = new StoUnitVo();
        vo1.setTag("D");
        vo1.setShelfName("整货货架");
        vo1.setChildren(mapList1);
        vos.add(vo);
        vos.add(vo1);
        return AjaxResult.success(vos);
    }

    @Override
    public AjaxResult querySkuByUnitCode(String unitCode) {
        return AjaxResult.success(yxlReportMapper.querySkuByUnitCode(unitCode));
    }

    @Override
    public AjaxResult queryUnitNum() {
        List<String> result = new ArrayList<>();
        List<HashMap<String, Object>> mapList = yxlReportMapper.selectUnitNum();
        StringBuilder sb = new StringBuilder();
        StringBuilder sb1 = new StringBuilder();
        // 用于记录每个库房的总剩余数量，判断是否需要提示补打货位
        mapList.forEach(item -> {
            String stoName = item.get("sto_name").toString();
            String stoAreaName = item.get("sto_area_name").toString();
            String unUseNumStr = item.get("unUseNum").toString();
            int unUseNum = Integer.parseInt(unUseNumStr);
            if (unUseNum <= 10) {
                sb.append("【").append(stoName).append(stoAreaName).append("架位号剩余").append(unUseNum).append("个】");
            } else {
                sb1.append("【").append(stoName).append(stoAreaName).append("架位号剩余").append(unUseNum).append("个】");
            }
        });
        sb.append(" 请尽快补打货位。");
        // 去除最后多余的逗号
        if (sb.length() > 0) {
            sb.deleteCharAt(sb.length() - 1);
        }
        if (sb1.length() > 0) {
            sb1.deleteCharAt(sb1.length() - 1);
        }
        result.add(sb.toString());
        result.add(sb1.toString());
        return AjaxResult.success(result);
    }
    //===============================公共方法===========================

    /**
     * 安全地将Map列表转换为ExportSkuAlarmsVo列表
     *
     * @param rows 原始数据行
     * @return 转换后的VO列表
     */
    private List<ExportWMSWarehouseOutDetailVo> convertAreaHouseDetailMapToVoList(Object rows) {
        if (rows == null) {
            return Collections.emptyList();
        }
        if (!(rows instanceof List<?>)) {
            log.error("期望List类型数据，实际类型: {}", rows.getClass().getName());
            return Collections.emptyList();
        }
        List<?> rowList = (List<?>) rows;
        List<ExportWMSWarehouseOutDetailVo> resultList = new ArrayList<>(rowList.size());

        for (Object item : rowList) {
            if (item instanceof Map<?, ?>) {
                Map<?, ?> rowMap = (Map<?, ?>) item;
                ExportWMSWarehouseOutDetailVo vo = new ExportWMSWarehouseOutDetailVo();
                vo.setBillsId(getStringValue(rowMap, "BillsId"));
                vo.setCheckDate(formatDate(rowMap.get("CheckDate")));
                vo.setSto_name(getStringValue(rowMap, "sto_name"));
                vo.setDrugName(getStringValue(rowMap, "DrugName"));
                vo.setDrugGG(getStringValue(rowMap, "DrugGG"));
                vo.setDrugNum(getStringValue(rowMap, "DrugNum"));
                vo.setDrugDw(getStringValue(rowMap, "DrugDw"));
                vo.setDrugCD(getStringValue(rowMap, "DrugCD"));
                vo.setDrugBatchNo(getStringValue(rowMap, "DrugBatchNo"));
                vo.setDrugPD(getStringValue(rowMap, "DrugPD"));
                vo.setDrugExp(getStringValue(rowMap, "DrugExp"));
                vo.setUserID(getStringValue(rowMap, "UserID"));
                resultList.add(vo);
            }
        }
        return resultList;
    }

    private List<ExportWMSWarehouseInDetailVo> convertAreaHouseInDetailMapToVoList(Object rows) {
        if (rows == null) {
            return Collections.emptyList();
        }
        if (!(rows instanceof List<?>)) {
            log.error("期望List类型数据，实际类型: {}", rows.getClass().getName());
            return Collections.emptyList();
        }
        List<?> rowList = (List<?>) rows;
        List<ExportWMSWarehouseInDetailVo> resultList = new ArrayList<>(rowList.size());

        for (Object item : rowList) {
            if (item instanceof Map<?, ?>) {
                Map<?, ?> rowMap = (Map<?, ?>) item;
                ExportWMSWarehouseInDetailVo vo = new ExportWMSWarehouseInDetailVo();
                vo.setBillsId(getStringValue(rowMap, "BillsId"));
                vo.setCheckDate(formatDate(rowMap.get("CheckDate")));
                vo.setSto_name(getStringValue(rowMap, "sto_name"));
                vo.setDrugName(getStringValue(rowMap, "DrugName"));
                vo.setDrugGG(getStringValue(rowMap, "DrugGG"));
                vo.setDrugNum(getStringValue(rowMap, "DrugNum"));
                vo.setDrugDw(getStringValue(rowMap, "DrugDw"));
                vo.setDrugCD(getStringValue(rowMap, "DrugCD"));
                vo.setDrugBatchNo(getStringValue(rowMap, "DrugBatchNo"));
                vo.setDrugPD(getStringValue(rowMap, "DrugPD"));
                vo.setDrugExp(getStringValue(rowMap, "DrugExp"));
                vo.setUserID(getStringValue(rowMap, "UserID"));
                vo.setDrugJWH(getStringValue(rowMap, "DrugJWH"));
                resultList.add(vo);
            }
        }
        return resultList;
    }

    /**
     * 从Map中获取字符串值，并进行空值处理
     */
    private String getStringValue(Map<?, ?> map, String key) {
        Object value = map.get(key);
        if (value == null) {
            return "";
        }
        return String.valueOf(value);
    }

    /**
     * 安全地格式化日期
     */
    private String formatDate(Object dateObj) {
        if (dateObj == null) {
            return "";
        }
        if (dateObj instanceof Date) {
            return DateUtils.parseDateToStr("yyyy-MM-dd HH:mm:ss", (Date) dateObj);
        }
        // 处理可能的时间戳或其他格式
        try {
            if (dateObj instanceof Number) {
                Date date = new Date(((Number) dateObj).longValue());
                return DateUtils.parseDateToStr("yyyy-MM-dd HH:mm:ss", date);
            }
            // 尝试解析字符串日期
            if (dateObj instanceof String) {
                Date date = DateUtils.parseDate(dateObj.toString());
                return DateUtils.parseDateToStr("yyyy-MM-dd HH:mm:ss", date);
            }
        } catch (Exception e) {
            log.warn("日期解析失败: {}", e.getMessage());
        }
        return "";
    }

    // 获取指定类的所有属性名
    private String[] getAllColumns(Class<?> clazz) {
        List<String> columns = new ArrayList<>();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            columns.add(field.getName());
        }
        return columns.toArray(new String[0]);
    }
}
